/*************************************************************************
 *
 *  Copyright (C) 2013 Florian Dang <florian.coin@gmail.com>
 *
 *  This file is part of NatusVerse.
 *
 *  NatusVerse is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  NatusVerse is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NatusVerse.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************/

#include "life/automaton.h"

#include <stdio.h>
#include <cassert>

#include "utils.h"

#define LIFE
//#define SEEDS

namespace life
{

void Automaton::Initialize(const int& win_w, const int& win_h)
{
	//printf("win_w / w_ = %d / %d win_h / h_ = %d / %d", win_w, w_, win_h, h_);

	assert((win_w % w_ == 0) && (win_h % h_) == 0);

	w_cell_ = win_w / w_;
	h_cell_ = win_h / h_;

	#pragma omp parallel for schedule(dynamic, 2)
	for (int i = 0; i < w_ * h_; i ++)
	{
#ifdef LIFE
		if(Rand::RFloat() < 0.08f)
			vec_cell_[i].set_state(Cell::kAlive);
#endif
#ifdef SEEDS
//		if(i > (w_ * h_ / 4) - 4 && i < (w_ * h_ / 4) + 4)
//			vec_cell_[i].set_state(Cell::kAlive);
		if(Rand::RFloat() < 0.006f)
			vec_cell_[i].set_state(Cell::kAlive);
#endif
		else
			vec_cell_[i].set_state(Cell::kDead);

		vec_cell_[i].set_x(i / w_);
		vec_cell_[i].set_y(i % w_);
		vec_cell_[i].set_color();
	}
}

void Automaton::CheckGrid(Window win)
{
	int winGetH = win.GetH();

	#pragma omp parallel for schedule(dynamic, 2)
	for (int idx = 0; idx < w_ * h_; idx ++)
	{
		int num_cell_alive = CheckNeighbors(idx);

		//DisplayCell(win, idx, winGetH);

// Life B3/S23
#ifdef LIFE
		if(vec_cell_[idx].state() == Cell::kAlive)
		{
			if(num_cell_alive != 2 && num_cell_alive != 3)
			{
#if DEBUG
				vec_cell_[idx].Print();
				printf(" alive:%d\n", cell_alive);
#endif
				vec_cell_[idx].set_state(Cell::kDead);
				//vec_cell_[idx].set_color(color::lightpink);
				DisplayCell(win, idx, winGetH);
			}
		}
		else
		{
			if(num_cell_alive == 3)
			{
#if DEBUG
				vec_cell_[idx].Print();
				printf(" alive:%d\n", cell_alive);
#endif
				vec_cell_[idx].set_state(Cell::kAlive);
				//vec_cell_[idx].set_color(color::aquamarine);
				DisplayCell(win, idx, winGetH);
			}
		}
#endif  // LIFE

// Seeds B2/S
#ifdef SEEDS
		if(vec_cell_[idx].state() == Cell::kAlive)
		{
			if(num_cell_alive != 2)
			{
#if DEBUG
				vec_cell_[idx].Print();
				printf(" alive:%d\n", cell_alive);
#endif
				vec_cell_[idx].set_state(Cell::kDead);
				//vec_cell_[idx].set_color(color::lightpink);
				DisplayCell(win, idx, winGetH);
			}
		}
		else
		{
			if(num_cell_alive == 2)
			{
#if DEBUG
				vec_cell_[idx].Print();
				printf(" alive:%d\n", cell_alive);
#endif
				vec_cell_[idx].set_state(Cell::kAlive);
				//vec_cell_[idx].set_color(color::aquamarine);
				DisplayCell(win, idx, winGetH);
			}
		}
#endif // SEEDS
	}
}

int Automaton::CheckNeighbors(const int& idx) const
{
	int i = idx / w_;
	int j = idx % w_;
	//int cell_dead = 0;
	int num_cell_alive = 0;

	if (j > 0)
	{
		//compare_parcelle(i, j, i, j - 1);
		if (vec_cell_[idx - 1].state() == Cell::kAlive)
			num_cell_alive ++;

		if (i > 0)
//			compare_parcelle(i, j, i - 1, j - 1);
			if (vec_cell_[idx - w_ - 1].state() == Cell::kAlive)
				num_cell_alive ++;

		if (i < h_ - 1)
//			compare_parcelle(i, j, i + 1, j - 1);
			if (vec_cell_[idx + w_ - 1].state() == Cell::kAlive)
				num_cell_alive ++;
	}

	if (j < w_ - 1)
	{
//		compare_parcelle(i, j, i, j + 1);
		if (vec_cell_[idx + 1].state() == Cell::kAlive)
				num_cell_alive ++;


		if (i > 0)
//			compare_parcelle(i, j, i - 1, j + 1);
			if (vec_cell_[idx - w_ + 1].state() == Cell::kAlive)
				num_cell_alive ++;

		if (i < h_ - 1)
//			compare_parcelle(i, j, i + 1, j + 1);
			if (vec_cell_[idx + w_ + 1].state() == Cell::kAlive)
				num_cell_alive ++;
	}

	if (i > 0)
//		compare_parcelle(i, j, i - 1, j);
		if (vec_cell_[idx - w_].state() == Cell::kAlive)
			num_cell_alive ++;


	if (i < h_ - 1)
//		compare_parcelle(i, j, i + 1, j);
		if (vec_cell_[idx + w_].state() == Cell::kAlive)
			num_cell_alive ++;

	return num_cell_alive;
}

void Automaton::Display(Window win) const
{
	typedef Window::t_point t_point;

	//t_point down_left = Graphics::kpoint_orig_t;
	t_point up_right, down_left;
	int win_h_minus_cell = win.GetH() - h_cell();
	int win_w_minus_cell = win.GetW();

	down_left.x = 0;
	up_right.x = w_cell();
	down_left.y = win_h_minus_cell;
	up_right.y = win_h_minus_cell + h_cell();

	#pragma omp parallel for schedule(dynamic, 2)
	for (int i = 0; i < h(); i ++)
	{
		for (int j = 0; j < w(); j ++)
		{
			vec_cell()[i * w() + j].Display(win, down_left, up_right);
#if DEBUG
			printf("\n(%d %d) at (%.1f, %.1f)  (%.1f, %.1f)   ", i, j,
					down_left.x, down_left.y, up_right.x, up_right.y);
			vec_cell()[i * w() + j].Print();
#endif
			down_left.x = (static_cast<int>(down_left.x) + w_cell()) % win_w_minus_cell;
			up_right.x = down_left.x + w_cell();
		}

		down_left.y = (static_cast<int>(down_left.y) - h_cell()) % win_h_minus_cell;
		up_right.y = down_left.y + h_cell();
	}

//	printf("WCell = %d HCell = %d", w_cell_, h_cell_);
}

void Automaton::DisplayCell(Window win, const int& idx, const int& winGetH)
{
	typedef Window::t_point t_point;

	int i = idx / w_;
	int j = idx % w_;

	t_point down_left;
	down_left.x = i * w_cell();
	down_left.y =  winGetH - (j + 1) * h_cell();

	t_point up_right = down_left;
	up_right.x += w_cell();
	up_right.y += h_cell();

	vec_cell()[idx].Display(win, down_left, up_right);
}

void Automaton::Print() const
{
	for (int i = 0; i < w_ * h_; i ++)
	{
		vec_cell_[i].Print();
		printf("\n");
	}
}
} // end namespace life
